package com.ex.mina;

import java.net.InetSocketAddress;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import com.ex.net.SocketListener;
import com.ex.util.LogUtil;
import com.example.androiddemo.Const;

/**
 * Mina 测试
 * @author thduan
 * 2015-9-18 10:52:44
 */
public class MinaService {
	private static final int MAX_RETRY_CNT = 3;
	private static final String END_BUF = "\r\n";
	
	private SocketListener mListener;
	private NioSocketConnector connector;
	private Thread socketThread;
	/** socket 会话 */
	private IoSession session;
	
	private ExecutorService sendService;
	/** 尝试重连次数：3 */
	private int curRetryCnt = 0;
	
	public MinaService() {
		sendService = Executors.newFixedThreadPool(2);
		init();
	}
	
	private void init() {
		//注意：重连时不能每次都 new NioSocketConnector,会导致服务端崩溃
		connector = new NioSocketConnector();
		connector.getFilterChain().addLast("logger", new LoggingFilter());
		connector.getSessionConfig().setReadBufferSize(2048);
		
		TextLineCodecFactory tcf = new TextLineCodecFactory(Charset.forName("UTF-8"));
		tcf.setDecoderMaxLineLength(Integer.MAX_VALUE);
		connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(tcf));
		MinaIoHandler ioHandler = new MinaIoHandler();
		
		connector.setHandler(ioHandler);
		connector.setConnectTimeoutMillis(3000);
	}
	
	public void startConn() {
		//连接放在子线程中
		socketThread = new Thread(new ConnRun());
		socketThread.start();
	}
	
	private class ConnRun implements Runnable {

		@Override
		public void run() {
			connLogic();
		}
		
	}
	
	private void connLogic() {
		ConnectFuture cf = connector.connect(new InetSocketAddress(Const.SOCKET_ADDRESS, Const.SOCKET_PORT));
		cf.awaitUninterruptibly();
		
		if(cf.isConnected()) {
			session = cf.getSession();
			session.write("连接成功咯" + END_BUF);
			LogUtil.mina("socket 连接成功");
			//重置重连次数
			curRetryCnt = 0;
		}else {
			LogUtil.mina("socket 连接失败");
			if(curRetryCnt < MAX_RETRY_CNT) {
				try {
					//5s 后尝试重连
					Thread.sleep(5 * 1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} 
				
				//FIXME, 尝试重连机制
				startConn();
				curRetryCnt++;
			}
		}
	}
	
	public void sendMsg(final String msg) {
		if(session == null) return;
		sendService.execute(new Runnable() {
			
			@Override
			public void run() {
				try {
					session.write(msg + "\r\n");
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}
	
	public void closeService() {
		connector.dispose();
	}
	
	/**
	 * 设置监听， 最好用 EventBus 发消息
	 * @param listener
	 */
	public void setSocketListener(SocketListener listener) {
		mListener = listener;
	}
	
	private class MinaIoHandler extends IoHandlerAdapter{
		
//		private SocketListener listener;
	//	
//		public MinaIoHandler(SocketListener listener) {
//			this.listener = listener;
//		}
		
		@Override
		public void sessionCreated(IoSession serssion) throws Exception {
			super.sessionCreated(session);
			LogUtil.mina("sessionCreated");
		}
		
		@Override
		public void sessionOpened(IoSession session) throws Exception {
			super.sessionOpened(session);
			LogUtil.mina("sessionOpened");
		}
		
		@Override
		public void messageReceived(IoSession session, Object message)
				throws Exception {
			super.messageReceived(session, message);
			LogUtil.mina("messageReceived : " + message);
			
		}
		
		@Override
		public void messageSent(IoSession session, Object message) throws Exception {
			super.messageSent(session, message);
			LogUtil.mina("messageSent: " + URLEncoder.encode((String)message, "UTF-8"));
		}
		
		@Override
		public void exceptionCaught(IoSession session, Throwable cause)
				throws Exception {
			super.exceptionCaught(session, cause);
			LogUtil.mina("exceptionCaught");
		}
		
		@Override
		public void sessionClosed(IoSession session) throws Exception {
			super.sessionClosed(session);
			LogUtil.mina("sessionClosed");
		}

	}

}
